Serverless Framework で resources に定義したリソースを環境変数で参照しようとした時に、エラーが発生した場合の対処方法
はじめに
こんにちは、サービスグロースチームの筧です。
Serverless Framework には resources というプロパティがあります。 このプロパティを使用すると、serverless deploy した際に、SNS トピック や S3 バケット などのリソースを作成することができます。
Serverless Framework - AWS Infrastructure Resources
If you are using AWS as a provider for your Service, all are other AWS infrastructure resources which the AWS Lambda functions in your depend on, like AWS DynamoDB or AWS S3. Using the Serverless Framework, you can define the infrastructure resources you need in serverless.yml, and easily deploy them.
では resources プロパティ経由で作成したリソースの情報を、 同じ Serverless Framework で定義している AWS Lambda の環境変数として参照するには、どうしたらよいでしょうか。 一つの方法として、cf:stackName.outputKey というシンタックスを利用する方法があります。
Serverless Framework Variables
Variables allow users to dynamically replace config values in serverless.yml config. They are especially useful when providing secrets for your service to use and when you are working with multiple stages. To use variables, you will need to reference values enclosed in ${} brackets.
このシンタックスを利用した方法を試したところ、初回デプロイ時以外は問題なく動作しました。
ただ初回デプロイ時のみ、Value not found at "cf" source
というエラーメッセージが表示されてデプロイ失敗になってしまいました。
原因調査したところ、以下の Issue を発見しました。
Default values for cf references · Issue #4940 · serverless/serverless
This is a (Bug Report / Feature Proposal) Using cf references it's not possible to provide default values. Example: ${cf:non-existing-stack.foo, 'bar'} Description For bug reports: What went wrong? Default value was not picked. Instead o...
どうやら、cf:stackName.outputKey シンタックスを利用する場合、初回デプロイ時のみ、依存関係サイクルの問題が発生してしまうようです。 そして Issue の中で紹介されていた方法の一つで、問題解消することができたので、その内容について本ブログでご紹介します。
前提
筆者の環境
% sw_vers ProductName: macOS ProductVersion: 12.2.1 BuildVersion: 21D62 % sls --version Framework Core: 3.19.0 (local) 3.19.0 (global) Plugin: 6.2.2 SDK: 4.3.2 % pipenv --version pipenv, version 2022.6.7
resources プロパティで SNS トピックを定義して、そのARNを環境変数で参照することをゴールとします
やってみた
失敗パターン: cf:stackName.outputKey シンタックスを利用して初回デプロイした場合
cf:stackName.outputKey シンタックスを利用して、resources の Outputs から SNS トピックの ARN を参照します。
service: hello frameworkVersion: '3' provider: name: aws runtime: python3.9 lambdaHashingVersion: 20201221 stage: ${opt:stage, 'dev'} region: ${opt:region, "ap-northeast-1"} : snip : environment: ${self:custom.environment.${self:provider.stage}} functions: hello: handler: src/handlers/hello.handler : snip : custom: environment: dev: SNS_TOPIC_ARN: ${cf:${self:service}-${self:provider.stage}.SnsTopicArn} : snip : resources: Resources: HelloTopic: Type: "AWS::SNS::Topic" Properties: DisplayName: "HelloTopic-${self:provider.stage}-${self:provider.region}" TopicName: "HelloTopic-${self:provider.stage}-${self:provider.region}" Outputs: SnsTopicArn: Value: !Ref HelloTopic Export: Name: SnsTopicArn
このような書き方だと、初回デプロイ時のみ、Value not found at "cf" source
というエラーメッセージが表示されてデプロイ失敗します。
% aws-vault exec personal -- sls deploy --stage dev Running "serverless" from node_modules Error: Cannot resolve serverless.yml: Variables resolution errored with: - Cannot resolve variable at "custom.environment.dev.SNS_TOPIC_ARN": Value not found at "cf" source
成功パターン: 環境変数で Ref を利用して初回デプロイした場合
Ref を利用して、resources の Resources から SNS トピックの ARN を参照します。
service: hello frameworkVersion: '3' provider: name: aws runtime: python3.9 lambdaHashingVersion: 20201221 stage: ${opt:stage, 'dev'} region: ${opt:region, "ap-northeast-1"} : snip : environment: ${self:custom.environment.${self:provider.stage}} functions: hello: handler: src/handlers/hello.handler : snip : custom: environment: dev: SNS_TOPIC_ARN: Ref: HelloTopic : snip : resources: Resources: HelloTopic: Type: "AWS::SNS::Topic" Properties: DisplayName: "HelloTopic-${self:provider.stage}-${self:provider.region}" TopicName: "HelloTopic-${self:provider.stage}-${self:provider.region}"
初回デプロイ時でも、問題なくデプロイできます。
% aws-vault exec personal -- sls deploy --stage dev Running "serverless" from node_modules Deploying hello to stage dev (ap-northeast-1) ✔ Service deployed to stack hello-dev (160s)
hello.py の handler 関数です。
import logging import os logger = logging.getLogger() logger.setLevel(logging.INFO) SNS_TOPIC_ARN = os.environ["SNS_TOPIC_ARN"] def handler(event, context): logger.info(SNS_TOPIC_ARN) return
hello.py の handler 関数を実行すると、resourcesで定義した SNS トピックの ARN が参照できていることを確認できました!
% aws-vault exec personal -- sls invoke local -f hello --stage dev Running "serverless" from node_modules INFO:root:arn:aws:sns:ap-northeast-1:123456789012:HelloTopic-dev-ap-northeast-1 null
おわりに
最後まで読んでいただきありがとうございます。
本ブログが少しでも皆さんの役立っていると幸いです。 それではまた!